package com.kpmg.datalake.common.service.impl;

import com.alibaba.druid.sql.parser.Token;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.kpmg.datalake.common.dynamicdatasource.DatabaseDTO;
import com.kpmg.datalake.common.dynamicdatasource.DynamicDataSourceContextHolder;
import com.kpmg.datalake.common.enums.ResultCodeEnum;
import com.kpmg.datalake.common.enums.SystemParamsEnum;
import com.kpmg.datalake.common.enums.TopicEnum;
import com.kpmg.datalake.common.enums.UseOrDelEnum;
import com.kpmg.datalake.common.exception.BusinessException;
import com.kpmg.datalake.common.model.IndustryConfig;
import com.kpmg.datalake.common.service.IComSystemParamsService;
import com.kpmg.datalake.common.service.IDbPrmsnService;
import com.kpmg.datalake.common.service.IEntBscinfService;
import com.kpmg.datalake.common.service.IIndustryConfigService;
import com.kpmg.datalake.common.service.SqlParseService;
import com.kpmg.datalake.common.utils.AssertUtil;
import com.kpmg.datalake.common.utils.DatabaseUtils;
import com.kpmg.datalake.common.utils.DateUtils;
import com.kpmg.datalake.common.utils.PinyinUtils;
import com.kpmg.datalake.common.utils.RandStrUtil;
import com.kpmg.datalake.common.vo.KeyAndValueDTO;
import com.kpmg.datalake.common.vo.ThreeParameter;
import com.kpmg.datalake.db.dao.DbPrmsnMapper;
import com.kpmg.datalake.db.model.DbPrmsn;
import com.kpmg.datalake.db.model.EntBscinf;
import com.kpmg.datalake.db.model.Project;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * @author Alex.zhao [alex.zhao@kpmg.com]
 * @since Created on azhao6 on 2018-08-20 18:09:32
 */
@Service
public class DbPrmsnServiceImpl extends
    ServiceImpl<DbPrmsnMapper, DbPrmsn> implements IDbPrmsnService {

  private static final Logger logger = LoggerFactory.getLogger(DbPrmsnServiceImpl.class);


  @Value("${spring.datasource.druid.url}")
  private String url;

  @Resource
  private IComSystemParamsService comSystemParamsService;
  @Resource
  private IEntBscinfService entBscinfService;
  @Resource
  private IIndustryConfigService industryConfigService;
  @Resource
  private SqlParseService sqlParseService;

  /**
   * 通过项目ID 查找数据库配置信息
   *
   * @param projectId 项目ID
   * @return 项目信息
   */
  @Override
  public DbPrmsn getDbPrmsnByProjectId(String projectId) {
    AssertUtil.assertNotEmpty(projectId, "项目ID不能为空");
    DbPrmsn dbPrmsn = new DbPrmsn();
    dbPrmsn.setPrjId(projectId);
    return super.selectOne(new EntityWrapper<>(dbPrmsn));
  }

  /**
   * 通过项目信息初始化 数据库连接信息
   *
   * @param project 项目信息
   * @return 数据库链接信息
   */
  @Override
  public DbPrmsn initDbPrmsnByProject(Project project) {
    DbPrmsn dbPrmsn = this.getDbPrmsnByProjectId(project.getPrjId());
    if (dbPrmsn == null) {
      dbPrmsn = new DbPrmsn();
      DatabaseDTO databaseDTO = DatabaseUtils.parseUrl(url);
      dbPrmsn.setPrjId(project.getPrjId());
      dbPrmsn.setDbTpnm(databaseDTO.getDbType());
      dbPrmsn.setIpAdr(databaseDTO.getIp());
      dbPrmsn.setPort(Integer.valueOf(databaseDTO.getPort()));
      dbPrmsn.setDbInstnnm(databaseDTO.getDbName());
      String dataLakePasswordLength = comSystemParamsService
          .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_PASSWORD_LENGTH.getCode(), "10");
      //生成10位随机密码
      dbPrmsn.setDbPwd(RandStrUtil.getComplexStr(Integer.valueOf(dataLakePasswordLength)));
      dbPrmsn.setDbAcc(this.getUserName(project));
      dbPrmsn.setDelInd(UseOrDelEnum.USE.getIntCode());
    } else {
      throw new BusinessException(ResultCodeEnum.CODE4012, "数据库配置信息已存在不能初始化");
    }
    return dbPrmsn;
  }

  /**
   * 初始化数据库配置信息
   *
   * @param dbPrmsn dbPrmsn
   */
  @Override
  public void initDbPrmsnDatabase(DbPrmsn dbPrmsn) {
    if (dbPrmsn == null) {
      throw new BusinessException(ResultCodeEnum.CODE4000, "不能为空");
    }
    LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
    //创建表空间
    String dataLakeCreateTablespace = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_CREATE_TABLE_SPACE.getCode());
    AssertUtil.assertNotEmpty(dataLakeCreateTablespace, "创建表空间配置不能为空");
    dataLakeCreateTablespace = String
        .format(dataLakeCreateTablespace, dbPrmsn.getDbAcc(), dbPrmsn.getDbAcc());
    logger.info(dataLakeCreateTablespace);
    linkedHashSet.add(dataLakeCreateTablespace);
    //创建临时表空间
    String dataLakeCreateTempTablespace = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_CREATE_TEMP_TABLE_SPACE.getCode());
    AssertUtil.assertNotEmpty(dataLakeCreateTempTablespace, "创建临时表空间不能为空");
    dataLakeCreateTempTablespace = String
        .format(dataLakeCreateTempTablespace, dbPrmsn.getDbAcc(), dbPrmsn.getDbAcc());
    logger.info(dataLakeCreateTempTablespace);
    linkedHashSet.add(dataLakeCreateTempTablespace);
    //新增额外表空间数量
    String dataLakeDefaultSpaceCount = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_DEFAULT_SPACE_COUNT.getCode());
    if (StringUtils.isNotBlank(dataLakeDefaultSpaceCount) && NumberUtils
        .isCreatable(dataLakeDefaultSpaceCount)) {
      //扩展表空间
      String dataLakeAlterTablespace = comSystemParamsService
          .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_ALTER_TABLE_SPACE.getCode());
      AssertUtil.assertNotEmpty(dataLakeAlterTablespace, "修改表空间不能为空");
      for (int i = 1; i <= Integer.valueOf(dataLakeDefaultSpaceCount); i++) {
        String alterTablespace = String
            .format(dataLakeAlterTablespace, dbPrmsn.getDbAcc(), dbPrmsn.getDbAcc(), i);
        logger.info(alterTablespace);
        linkedHashSet.add(alterTablespace);
      }
    }
    //创建用户语言
    String dataLakeCreateUser = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_CREATE_USER.getCode());
    AssertUtil.assertNotEmpty(dataLakeCreateUser, "创建用户配置不能为空");
    dataLakeCreateUser = String
        .format(dataLakeCreateUser, dbPrmsn.getDbAcc(), dbPrmsn.getDbPwd(), dbPrmsn.getDbAcc(),
            dbPrmsn.getDbAcc());
    logger.info(dataLakeCreateUser);
    linkedHashSet.add(dataLakeCreateUser);
    //用户授权
    String dataLakeUserGrant = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_USER_GRANT.getCode());
    AssertUtil.assertNotEmpty(dataLakeUserGrant, "用户授权配置不能为空");
    dataLakeUserGrant = String.format(dataLakeUserGrant, dbPrmsn.getDbAcc());
    logger.info(dataLakeUserGrant);
    linkedHashSet.add(dataLakeUserGrant);
    //处理扩展sql
    this.handExtendSQL(linkedHashSet, dbPrmsn.getDbAcc());
    for (String execSql : linkedHashSet) {
      if (execSql.endsWith(";")) {
        execSql = execSql.substring(0, execSql.length() - 1);
      }
      Integer result = super.baseMapper.executeSQL(execSql);
      logger.info("{} ----- {}", execSql, result);
    }
  }

  /**
   * 处理扩展sql
   *
   * @param linkedHashSet linkedHashSet
   */
  private void handExtendSQL(LinkedHashSet<String> linkedHashSet, String userName) {
    // dataLake 扩展sql
    String dataLakeExtendSql = comSystemParamsService
        .getSystemParamsByCode(SystemParamsEnum.DATA_LAKE_EXTEND_SQL.getCode());
    if (StringUtils.isNotBlank(dataLakeExtendSql)) {
      ThreeParameter<Boolean, List<KeyAndValueDTO<Token, String>>, String> result = sqlParseService
          .parseStringToSql(dataLakeExtendSql);
      if (result.getKey()) {
        for (KeyAndValueDTO<Token, String> kav : result.getValue()) {
          if (Token.SELECT == kav.getKey()) {
            //查询
            logger.error("该 sql：{} 未执行", kav.getValue());
          } else {
            String extendSql = String.format(kav.getValue().replaceAll("\\?", "%s"), userName);
            logger.info("extend sql ：{}", extendSql);
            linkedHashSet.add(extendSql);
          }
        }
      } else {
        throw new BusinessException(ResultCodeEnum.CODE4015, result.getMsg());
      }

    }
  }


  /**
   * 初始化行业化配置信息
   *
   * @param project 项目信息
   * @param isModule 是否初始化项目
   */
  @Override
  public void initIndustryInfo(Project project, Boolean isModule, TopicEnum topic) {
    AssertUtil.assertNotEmpty(project.getPrjId(), "请选择项目ID 信息");
    AssertUtil.assertNotEmpty(project.getEntId(), "获取项目行业信息出错");
    EntBscinf entBscinf = entBscinfService.selectById(project.getEntId());
    if (entBscinf == null) {
      throw new BusinessException(ResultCodeEnum.CODE4000.getCode(), "获取项目实体出错，项目实体不能为空");
    }
    //如果topic为空则表示使用相关 业务topic
    List<IndustryConfig> listByIdyIdList = industryConfigService
        .getListByIdyId(entBscinf.getIdyId(), topic);
    if (CollectionUtils.isEmpty(listByIdyIdList) && topic == null) {
      throw new BusinessException(ResultCodeEnum.CODE4000.getCode(),
          String.format("获取项目行业【%s】失败，行业化配置模板不能为空", entBscinf.getIdyId()));
    } else if (CollectionUtils.isEmpty(listByIdyIdList)) {
      return;
    }

    //行业模板sql解析
    List<KeyAndValueDTO<Token, String>> arrayList = new ArrayList<>();
    for (IndustryConfig industryConfig : listByIdyIdList) {
      ThreeParameter<Boolean, List<KeyAndValueDTO<Token, String>>, String> threeParameter;
      if (isModule) {
        threeParameter = sqlParseService.parseStringToSql(industryConfig.getIndustryModule());
      } else {
        //如果没有sql 默认就是失败
        if (StringUtils.isNotBlank(industryConfig.getIndustryHanler())) {
          threeParameter = sqlParseService.parseStringToSql(industryConfig.getIndustryHanler());
        } else {
          threeParameter = new ThreeParameter<>(false, null, null);
        }
      }

      if (threeParameter.getKey()) {
        arrayList.addAll(threeParameter.getValue());
      }
    }
    //sql 执行行业模板初始化
    sqlParseService.executeSQL(project.getPrjId(), arrayList, topic == null);

  }

  /**
   * 通过表名查询
   *
   * @param projectId 数据源ID
   * @param tableName 表面
   * @return 时间字段
   */
  @Override
  public Set<String> getDateColumn(String projectId, String tableName) {
    Set<String> hashSet = new HashSet<>();
    try {
      //切换多数据原查询
      DynamicDataSourceContextHolder.setDataSourceType(projectId);
      List<Map<String, Object>> dateColumn = super.baseMapper.getDateColumn(tableName);
      for (Map<String, Object> column : dateColumn) {
        hashSet.add(column.get("COLUMN_NAME").toString().toUpperCase());
      }
    } finally {
      DynamicDataSourceContextHolder.clearDataSource();
    }
    return hashSet;
  }

  /**
   * 根据项目获取用户名
   *
   * @param project project
   * @return String
   */
  private String getUserName(Project project) {
    String prjName = PinyinUtils.getAlpha(project.getPrjName());
    prjName = prjName.length() > 6 ? prjName.substring(1, 6) : prjName;
    prjName = prjName + DateUtils.format(project.getCrtDt(), DateUtils.YYYYMMDD);
    //校验数据库是否已存在该用户
    if (super.baseMapper.hasUser(prjName) > 0) {
      throw new BusinessException(ResultCodeEnum.CODE4026, "用户已存在");
    }
    return prjName;
  }


}
